#region Copyright (c) 2006-2011 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2011 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Linq;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Models;
using System.Text;
using Widgetsphere.Generator.Common.Util;
using System.Collections.Generic;

namespace Widgetsphere.Generator.EFSelfTrackingClient.Generators.EFCSDL
{
	public class EntityGeneratedTemplate : EFSelfTrackingClientBaseTemplate
	{
		private StringBuilder sb = new StringBuilder();
		private Table _currentTable;

		public EntityGeneratedTemplate(ModelRoot model, Table currentTable)
		{
			_model = model;
			_currentTable = currentTable;
		}

		#region BaseClassTemplate overrides
		public override string FileName
		{
			get { return string.Format("{0}.Generated.cs", _currentTable.PascalName); }
		}

		public string ParentItemName
		{
			get { return string.Format("{0}.cs", _currentTable.PascalName); }
		}

		public override string FileContent
		{
			get
			{
				try
				{
					sb = new StringBuilder();
					this.GenerateContent();
					return sb.ToString();
				}
				catch (Exception ex)
				{
					throw;
				}
			}
		}
		#endregion

		#region GenerateContent

		private void GenerateContent()
		{
			try
			{
				Widgetsphere.Generator.GenerationHelper.AppendCopyrightInCode(sb, _model);
				this.AppendUsingStatements();
				sb.AppendLine("namespace " + this.GetLocalNamespace());
				sb.AppendLine("{");
				this.AppendEntityClass();
				sb.AppendLine("}");
				sb.AppendLine();
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		private void AppendUsingStatements()
		{
			sb.AppendLine("using System;");
			sb.AppendLine("using System.Collections.Generic;");
			sb.AppendLine("using System.Collections.ObjectModel;");
			sb.AppendLine("using System.Collections.Specialized;");
			sb.AppendLine("using System.ComponentModel;");
			sb.AppendLine("using System.Globalization;");
			sb.AppendLine("using System.Runtime.Serialization;");
			sb.AppendLine();
		}

		private void AppendEntityClass()
		{
			sb.AppendLine("	/// <summary>");
			sb.AppendLine("	/// The collection to hold '" + _currentTable.PascalName + "' entities");
			if (!string.IsNullOrEmpty(_currentTable.Description))
				sb.AppendLine("	/// " + _currentTable.Description);
			sb.AppendLine("	/// </summary>");
			sb.AppendLine("	[DataContract(IsReference = true)]");
			//TODO
			//sb.AppendLine("	[KnownType(typeof(PersonAddress))]"); 

			if (_currentTable.ParentTable == null)
				sb.AppendLine("	public partial class " + _currentTable.PascalName + " : IObjectWithChangeTracker, INotifyPropertyChanged");
			else
				sb.AppendLine("	public partial class " + _currentTable.PascalName + " : " + this.GetLocalNamespace() + "." + _currentTable.ParentTable.PascalName + ", IObjectWithChangeTracker, INotifyPropertyChanged");

			sb.AppendLine("	{");
			this.AppendProperties();
			this.AppendIsEquivalent();
			this.AppendNavigationProperties();
			this.AppendChangeTracking();
			sb.AppendLine("	}");
			sb.AppendLine();
		}

		private void AppendIsEquivalent()
		{
			sb.AppendLine("		#region IsEquivalent");
			sb.AppendLine();
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Determines if all of the fields for the specified object exactly matches the current object.");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		/// <param name=\"item\">The object to compare</param>");
			sb.AppendLine("		public virtual bool IsEquivalent(object item)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (item == null) return false;");
			sb.AppendLine("			if (!(item is " + this.GetLocalNamespace() + "." + _currentTable.PascalName + ")) return false;");
			sb.AppendLine("			" + this.GetLocalNamespace() + "." + _currentTable.PascalName + " o = item as " + this.GetLocalNamespace() + "." + _currentTable.PascalName + ";");
			sb.AppendLine("			return (");

			IEnumerable<Column> allColumns = _currentTable.GetColumnsFullHierarchy(true).Where(x => x.Generated);
			int index = 0;
			foreach (Column column in allColumns)
			{
				Table typeTable = _currentTable.GetRelatedTypeTableByColumn(column, true);
				if (typeTable != null)
				{
					//sb.Append("				o." + typeTable.PascalName + " == this." + typeTable.PascalName);
					sb.Append("				o." + column.PascalName + " == this." + column.PascalName);
				}
				else
				{
					sb.Append("				o." + column.PascalName + " == this." + column.PascalName);
				}
				if (index < allColumns.Count() - 1) sb.Append(" &&");
				sb.AppendLine();
				index++;
			}

			sb.AppendLine("				);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		#endregion");
			sb.AppendLine();
		}

		private void AppendProperties()
		{
			sb.AppendLine("		#region Properties");
			sb.AppendLine();

			foreach (Column column in _currentTable.GetColumns().Where(x => x.Generated).OrderBy(x => x.Name))
			{
				if (column.PrimaryKey && _currentTable.ParentTable != null)
				{
					sb.AppendLine("		[DataMember]");
					sb.AppendLine("		public virtual " + column.GetCodeType() + " " + column.PascalName);
					sb.AppendLine("		{");
					sb.AppendLine("			get { return _" + column.CamelName + "; }");
					sb.AppendLine("			set");
					sb.AppendLine("			{");
					sb.AppendLine("				if (_" + column.CamelName + " != value)");
					sb.AppendLine("				{");
					sb.AppendLine("					if (ChangeTracker.ChangeTrackingEnabled && ChangeTracker.State != ObjectState.Added)");
					sb.AppendLine("					{");
					sb.AppendLine("						throw new InvalidOperationException(\"The property '" + column.PascalName + "' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state.\");");
					sb.AppendLine("					}");
					sb.AppendLine("					_" + column.CamelName + " = value;");
					sb.AppendLine("					OnPropertyChanged(\"" + column.PascalName + "\");");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine("		private " + column.GetCodeType() + " _" + column.CamelName + ";");
				}
				//else if (_currentTable.IsColumnRelatedToTypeTable(column))
				//{
				//  Table typeTable = _currentTable.GetRelatedTypeTableByColumn(column);

				//  //If this column is a type table column then generate a special property
				//  sb.AppendLine("		/// <summary>");
				//  if (!string.IsNullOrEmpty(column.Description))
				//    sb.AppendLine("		/// " + column.Description + "");
				//  else
				//    sb.AppendLine("		/// The property that maps back to the database '" + (column.ParentTableRef.Object as Table).DatabaseName + "." + column.DatabaseName + "' field");
				//  sb.AppendLine("		/// </summary>");
				//  sb.AppendLine("		/// <remarks>" + column.GetIntellisenseRemarks() + "</remarks>");
				//  sb.AppendLine("		[DataMember]");

				//  //sb.AppendLine("		public virtual " + typeTable.PascalName + "Constants " + typeTable.PascalName + " { get; set; }");
				//  sb.AppendLine("		public virtual " + column.GetCodeType() + " " + column.PascalName + " { get; set; }");

				//  sb.AppendLine();
				//}
				else
				{
					sb.AppendLine("		/// <summary>");
					if (!string.IsNullOrEmpty(column.Description))
						sb.AppendLine("		/// " + column.Description + "");
					else
						sb.AppendLine("		/// The property that maps back to the database '" + (column.ParentTableRef.Object as Table).DatabaseName + "." + column.DatabaseName + "' field");
					sb.AppendLine("		/// </summary>");
					sb.AppendLine("		/// <remarks>" + column.GetIntellisenseRemarks() + "</remarks>");
					sb.AppendLine("		[DataMember]");
					sb.AppendLine("		public virtual " + column.GetCodeType() + " " + column.PascalName + "");
					sb.AppendLine("		{");
					sb.AppendLine("			get { return _" + column.CamelName + "; }");
					sb.AppendLine("			set");
					sb.AppendLine("			{");
					sb.AppendLine("				if (_" + column.CamelName + " != value)");
					sb.AppendLine("				{");
					sb.AppendLine("					_" + column.CamelName + " = value;");
					sb.AppendLine("					OnPropertyChanged(\"" + column.PascalName + "\");");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine("		private " + column.GetCodeType() + " _" + column.CamelName + ";");
					sb.AppendLine();
				}
			}

			sb.AppendLine("		#endregion");
			sb.AppendLine();
		}

		private void AppendNavigationProperties()
		{
			sb.AppendLine("		#region Navigation Properties");
			sb.AppendLine();

			#region Parent Relations
			IEnumerable<Relation> relationList = _currentTable.GetRelations().AsEnumerable();
			foreach (Relation relation in relationList)
			{
				//If not both generated then do not process this code block
				if (!relation.ParentTable.Generated || !relation.ChildTable.Generated)
				{
					//Do Nothing
				}

				//inheritance relationship
				else if (relation.ParentTable == relation.ChildTable.ParentTable && relation.IsOneToOne)
				{
					//Do Nothing
				}

				//Do not walk to type tables
				//else if (relation.ParentTable.IsTypeTable || relation.ChildTable.IsTypeTable)
				//{
				//  //Do Nothing
				//}

				//1-1 relations
				else if (relation.IsOneToOne)
				{
					var fixupProcName = "Fixup" + relation.PascalRoleName + relation.ChildTable.PascalName;

					sb.AppendLine("		/// <summary>");
					sb.AppendLine("		/// The navigation definition for walking " + relation.ParentTable.PascalName + "->" + relation.ChildTable.PascalName + (string.IsNullOrEmpty(relation.PascalRoleName) ? "" : " (role: '" + relation.PascalRoleName + "')"));
					sb.AppendLine("		/// </summary>");
					sb.AppendLine("		[DataMember]");
					sb.AppendLine("		public virtual " + this.GetLocalNamespace() + ".TrackableCollection<" + this.GetLocalNamespace() + "." + relation.ChildTable.PascalName + "> " + relation.PascalRoleName + relation.ChildTable.PascalName + "Item");
					sb.AppendLine("		{");
					sb.AppendLine("			get");
					sb.AppendLine("			{");
					sb.AppendLine("				if (_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List == null)");
					sb.AppendLine("				{");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List = new " + this.GetLocalNamespace() + ".TrackableCollection<" + relation.ChildTable.PascalName + ">();");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List.CollectionChanged += " + fixupProcName + ";");
					sb.AppendLine("				}");
					sb.AppendLine("				return _" + relation.PascalRoleName + relation.ChildTable.CamelName + "List;");
					sb.AppendLine("			}");
					sb.AppendLine("			set");
					sb.AppendLine("			{");
					sb.AppendLine("				if (!ReferenceEquals(_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List, value))");
					sb.AppendLine("				{");
					sb.AppendLine("					if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("					{");
					sb.AppendLine("						throw new InvalidOperationException(\"Cannot set the FixupChangeTrackingCollection when ChangeTracking is enabled\");");
					sb.AppendLine("					}");
					sb.AppendLine("					if (_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List != null)");
					sb.AppendLine("					{");
					sb.AppendLine("						_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List.CollectionChanged -= " + fixupProcName + ";");
					sb.AppendLine("					}");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List = value;");
					sb.AppendLine("					if (_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List != null)");
					sb.AppendLine("					{");
					sb.AppendLine("						_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List.CollectionChanged += " + fixupProcName + ";");
					sb.AppendLine("					}");
					sb.AppendLine("					OnNavigationPropertyChanged(\"" + relation.PascalRoleName + relation.ChildTable.PascalName + "Item\");");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine("		private " + this.GetLocalNamespace() + ".TrackableCollection<" + relation.ChildTable.PascalName + "> _" + relation.PascalRoleName + relation.ChildTable.CamelName + "List;");
					sb.AppendLine();

					//Fixup Procedure
					sb.AppendLine("		private void " + fixupProcName + "(object sender, NotifyCollectionChangedEventArgs e)");
					sb.AppendLine("		{");
					sb.AppendLine("			if (IsDeserializing)");
					sb.AppendLine("			{");
					sb.AppendLine("				return;");
					sb.AppendLine("			}");
					sb.AppendLine();
					sb.AppendLine("			if (e.NewItems != null)");
					sb.AppendLine("			{");
					sb.AppendLine("				foreach (" + relation.ChildTable.PascalName + " item in e.NewItems)");
					sb.AppendLine("				{");
					sb.AppendLine("					item." + relation.PascalRoleName + relation.ParentTable.PascalName + "Item = this;");
					sb.AppendLine("					if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("					{");
					sb.AppendLine("						if (!item.ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("						{");
					sb.AppendLine("							item.StartTracking();");
					sb.AppendLine("						}");
					sb.AppendLine("						ChangeTracker.RecordAdditionToCollectionProperties(\"" + relation.PascalRoleName + relation.ChildTable.PascalName + "Item\", item);");
					sb.AppendLine("					}");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine();
					sb.AppendLine("			if (e.OldItems != null)");
					sb.AppendLine("			{");
					sb.AppendLine("				foreach (" + relation.ChildTable.PascalName + " item in e.OldItems)");
					sb.AppendLine("				{");
					sb.AppendLine("					if (ReferenceEquals(item." + relation.PascalRoleName + relation.ParentTable.PascalName + "Item, this))");
					sb.AppendLine("					{");
					sb.AppendLine("						item." + relation.PascalRoleName + relation.ParentTable.PascalName + "Item = null;");
					sb.AppendLine("					}");
					sb.AppendLine("					if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("					{");
					sb.AppendLine("						ChangeTracker.RecordRemovalFromCollectionProperties(\"" + relation.PascalRoleName + relation.ChildTable.PascalName + "Item\", item);");
					sb.AppendLine("					}");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine();
				}

				//Process the associative tables
				else if (relation.ChildTable.AssociativeTable)
				{
					IEnumerable<Relation> associativeRelations = relation.ChildTable.GetRelationsWhereChild();
					Relation targetRelation = null;
					Relation otherRelation = null;
					Relation relation1 = associativeRelations.First();
					Relation relation2 = associativeRelations.Last();
					if (_currentTable == ((Table)relation1.ParentTableRef.Object)) targetRelation = relation2;
					else targetRelation = relation1;
					if (targetRelation == relation2) otherRelation = relation1;
					else otherRelation = relation2;
					Table targetTable = (Table)targetRelation.ParentTableRef.Object;

					if (targetTable.Generated && !targetTable.IsTypeTable)
					{
						sb.AppendLine("		/// <summary>");
						sb.AppendLine("		/// The navigation definition for walking " + _currentTable.PascalName + "->" + targetTable.PascalName + (string.IsNullOrEmpty(otherRelation.PascalRoleName) ? "" : " (role: '" + otherRelation.PascalRoleName + "')"));
						sb.AppendLine("		/// </summary>");
						sb.AppendLine("		[DataMember]");
						sb.AppendLine("		public virtual ICollection<" + this.GetLocalNamespace() + "." + targetTable.PascalName + "> " + targetTable.PascalName + "List { get; set; }");
						sb.AppendLine();
					}
				}

				//Process relations where Current Table is the parent
				else if (relation.ParentTable == _currentTable && relation.ParentTable.Generated && relation.ChildTable.Generated && !relation.ChildTable.AssociativeTable)
				{
					var fixupProcName = "Fixup" + relation.PascalRoleName + relation.ChildTable.PascalName + "List";

					sb.AppendLine("		/// <summary>");
					sb.AppendLine("		/// The navigation definition for walking " + relation.ParentTable.PascalName + "->" + relation.ChildTable.PascalName + (string.IsNullOrEmpty(relation.PascalRoleName) ? "" : " (role: '" + relation.PascalRoleName + "')"));
					sb.AppendLine("		/// </summary>");
					sb.AppendLine("		[DataMember]");
					sb.AppendLine("		public virtual " + this.GetLocalNamespace() + ".TrackableCollection<" + this.GetLocalNamespace() + "." + relation.ChildTable.PascalName + "> " + relation.PascalRoleName + relation.ChildTable.PascalName + "List");
					sb.AppendLine("		{");
					sb.AppendLine("			get");
					sb.AppendLine("			{");
					sb.AppendLine("				if (_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List == null)");
					sb.AppendLine("				{");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List = new " + this.GetLocalNamespace() + ".TrackableCollection<" + relation.ChildTable.PascalName + ">();");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List.CollectionChanged += this." + fixupProcName + ";");
					sb.AppendLine("				}");
					sb.AppendLine("				return _" + relation.PascalRoleName + relation.ChildTable.CamelName + "List;");
					sb.AppendLine("			}");
					sb.AppendLine("			set");
					sb.AppendLine("			{");
					sb.AppendLine("				if (!ReferenceEquals(_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List, value))");
					sb.AppendLine("				{");
					sb.AppendLine("					if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("					{");
					sb.AppendLine("						throw new InvalidOperationException(\"Cannot set the FixupChangeTrackingCollection when ChangeTracking is enabled\");");
					sb.AppendLine("					}");
					sb.AppendLine("					if (_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List != null)");
					sb.AppendLine("					{");
					sb.AppendLine("						_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List.CollectionChanged -= this." + fixupProcName + ";");
					sb.AppendLine("					}");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List = value;");
					sb.AppendLine("					if (_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List != null)");
					sb.AppendLine("					{");
					sb.AppendLine("						_" + relation.PascalRoleName + relation.ChildTable.CamelName + "List.CollectionChanged += this." + fixupProcName + ";");
					sb.AppendLine("					}");
					sb.AppendLine("					OnNavigationPropertyChanged(\"" + relation.PascalRoleName + relation.ChildTable.PascalName + "List\");");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine("		private " + this.GetLocalNamespace() + ".TrackableCollection<" + this.GetLocalNamespace() + "." + relation.ChildTable.PascalName + "> _" + relation.PascalRoleName + relation.ChildTable.CamelName + "List;");
					sb.AppendLine();

					//Fixup Procedure
					sb.AppendLine("		private void " + fixupProcName + "(object sender, NotifyCollectionChangedEventArgs e)");
					sb.AppendLine("		{");
					sb.AppendLine("			if (IsDeserializing)");
					sb.AppendLine("			{");
					sb.AppendLine("				return;");
					sb.AppendLine("			}");
					sb.AppendLine();
					sb.AppendLine("			if (e.NewItems != null)");
					sb.AppendLine("			{");
					sb.AppendLine("				foreach (" + relation.ChildTable.PascalName + " item in e.NewItems)");
					sb.AppendLine("				{");
					sb.AppendLine("					item." + relation.PascalRoleName + relation.ParentTable.PascalName + "Item = this;");
					sb.AppendLine("					if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("					{");
					sb.AppendLine("						if (!item.ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("						{");
					sb.AppendLine("							item.StartTracking();");
					sb.AppendLine("						}");
					sb.AppendLine("						ChangeTracker.RecordAdditionToCollectionProperties(\"" + relation.PascalRoleName + relation.ChildTable.PascalName + "List\", item);");
					sb.AppendLine("					}");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine();
					sb.AppendLine("			if (e.OldItems != null)");
					sb.AppendLine("			{");
					sb.AppendLine("				foreach (" + relation.ChildTable.PascalName + " item in e.OldItems)");
					sb.AppendLine("				{");
					sb.AppendLine("					if (ReferenceEquals(item." + relation.PascalRoleName + relation.ParentTable.PascalName + "Item, this))");
					sb.AppendLine("					{");
					sb.AppendLine("						item." + relation.PascalRoleName + relation.ParentTable.PascalName + "Item = null;");
					sb.AppendLine("					}");
					sb.AppendLine("					if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("					{");
					sb.AppendLine("						ChangeTracker.RecordRemovalFromCollectionProperties(\"" + relation.PascalRoleName + relation.ChildTable.PascalName + "List\", item);");
					sb.AppendLine("					}");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine();
				}

			}
			#endregion

			#region Child Relations
			relationList = _currentTable.GetRelationsWhereChild().AsEnumerable();
			foreach (Relation relation in relationList)
			{
				//Do not walk to associative
				//if (relation.ParentTable.IsTypeTable || relation.ChildTable.IsTypeTable)
				//{
				//  //Do Nothing
				//}

				//inheritance relationship
				if (relation.ParentTable == relation.ChildTable.ParentTable && relation.IsOneToOne)
				{
					//Do Nothing
				}

				//A 1:1 relation
				else if(relation.IsOneToOne)
				{
					var fixupProcName = "Fixup" + relation.PascalRoleName + relation.ParentTable.PascalName;

					sb.AppendLine("		/// <summary>");
					sb.AppendLine("		/// The navigation definition for walking " + relation.ParentTable.PascalName + "->" + relation.ChildTable.PascalName + (string.IsNullOrEmpty(relation.PascalRoleName) ? "" : " (role: '" + relation.PascalRoleName + "')"));
					sb.AppendLine("		/// </summary>");
					sb.AppendLine("		[DataMember]");
					sb.AppendLine("		public virtual " + relation.ParentTable.PascalName + " " + relation.PascalRoleName + relation.ParentTable.PascalName + "Item");
					sb.AppendLine("		{");
					sb.AppendLine("			get { return _" + relation.PascalRoleName + relation.ParentTable.CamelName + "; }");
					sb.AppendLine("			set");
					sb.AppendLine("			{");
					sb.AppendLine("				if (!ReferenceEquals(_" + relation.PascalRoleName + relation.ParentTable.CamelName + ", value))");
					sb.AppendLine("				{");
					sb.AppendLine("					var previousValue = _" + relation.PascalRoleName + relation.ParentTable.CamelName + ";");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ParentTable.CamelName + " = value;");
					sb.AppendLine("					this." + fixupProcName + "(previousValue);");
					sb.AppendLine("					OnNavigationPropertyChanged(\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\");");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine("		private " + relation.ParentTable.PascalName + " _" + relation.PascalRoleName + relation.ParentTable.CamelName + ";"); ;
					sb.AppendLine();

					sb.AppendLine("		private void " + fixupProcName + "(" + relation.ParentTable.PascalName + " previousValue, bool skipKeys = false)");
					sb.AppendLine("		{");
					sb.AppendLine("			if (IsDeserializing)");
					sb.AppendLine("			{");
					sb.AppendLine("				return;");
					sb.AppendLine("			}");
					sb.AppendLine();

					sb.AppendLine("			if (previousValue != null && previousValue." + relation.PascalRoleName + relation.ChildTable.PascalName + "Item.Contains(this))");
					sb.AppendLine("			{");
					sb.AppendLine("				previousValue." + relation.PascalRoleName + relation.ChildTable.PascalName + "Item.Remove(this);");
					sb.AppendLine("			}");

					sb.AppendLine();
					sb.AppendLine("			if (" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item != null)");
					sb.AppendLine("			{");
					sb.AppendLine("				if (!" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item." + relation.PascalRoleName + relation.ChildTable.PascalName + "Item.Contains(this))");
					sb.AppendLine("				{");
					sb.AppendLine("					" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item." + relation.PascalRoleName + relation.ChildTable.PascalName + "Item.Add(this);");
					sb.AppendLine("				}");
					sb.AppendLine();

					foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
					{
						sb.AppendLine("				" + columnRelationship.ChildColumn.PascalName + " = " + relation.PascalRoleName + relation.ParentTable.PascalName + "Item." + columnRelationship.ParentColumn.PascalName + ";");
					}

					sb.AppendLine("			}");
					sb.AppendLine("			else if (!skipKeys)");
					sb.AppendLine("			{");

					foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
					{
						if (columnRelationship.ChildColumn.AllowNull)
							sb.AppendLine("				" + columnRelationship.ChildColumn.PascalName + " = null;");
					}

					sb.AppendLine("			}");
					sb.AppendLine();
					sb.AppendLine("			if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("			{");
					sb.AppendLine("				if (ChangeTracker.OriginalValues.ContainsKey(\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\")");
					sb.AppendLine("						&& (ChangeTracker.OriginalValues[\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\"] == " + relation.PascalRoleName + relation.ParentTable.PascalName + "Item))");
					sb.AppendLine("				{");
					sb.AppendLine("					ChangeTracker.OriginalValues.Remove(\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\");");
					sb.AppendLine("				}");
					sb.AppendLine("				else");
					sb.AppendLine("				{");
					sb.AppendLine("					ChangeTracker.RecordOriginalValue(\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\", previousValue);");
					sb.AppendLine("				}");
					sb.AppendLine("				if (" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item != null && !" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item.ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("				{");
					sb.AppendLine("					" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item.StartTracking();");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine();
				}

				//Process relations where Current Table is the child
				else if (relation.ChildTable == _currentTable && relation.ParentTable.Generated && relation.ChildTable.Generated)
				{
					var fixupProcName = "Fixup" + relation.PascalRoleName + relation.ParentTable.PascalName;

					sb.AppendLine("		/// <summary>");
					sb.AppendLine("		/// The navigation definition for walking " + relation.ParentTable.PascalName + "->" + relation.ChildTable.PascalName + (string.IsNullOrEmpty(relation.PascalRoleName) ? "" : " (role: '" + relation.PascalRoleName + "')"));
					sb.AppendLine("		/// </summary>");
					sb.AppendLine("		[DataMember]");
					sb.AppendLine("		public virtual " + relation.ParentTable.PascalName + " " + relation.PascalRoleName + relation.ParentTable.PascalName + "Item");
					sb.AppendLine("		{");
					sb.AppendLine("			get { return _" + relation.PascalRoleName + relation.ParentTable.CamelName + "; }");
					sb.AppendLine("			set");
					sb.AppendLine("			{");
					sb.AppendLine("				if (!ReferenceEquals(_" + relation.PascalRoleName + relation.ParentTable.CamelName + ", value))");
					sb.AppendLine("				{");
					sb.AppendLine("					var previousValue = _" + relation.PascalRoleName + relation.ParentTable.CamelName + ";");
					sb.AppendLine("					_" + relation.PascalRoleName + relation.ParentTable.CamelName + " = value;");
					sb.AppendLine("					this." + fixupProcName + "(previousValue);");
					sb.AppendLine("					OnNavigationPropertyChanged(\"" + relation.PascalRoleName + relation.ParentTable.PascalName +"\");");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine("		private " + relation.ParentTable.PascalName + " _" + relation.PascalRoleName + relation.ParentTable.CamelName + ";"); ;
					sb.AppendLine();

					sb.AppendLine("		private void " + fixupProcName + "(" + relation.ParentTable.PascalName + " previousValue, bool skipKeys = false)");
					sb.AppendLine("		{");
					sb.AppendLine("			if (IsDeserializing)");
					sb.AppendLine("			{");
					sb.AppendLine("				return;");
					sb.AppendLine("			}");
					sb.AppendLine();

					sb.AppendLine("			if (previousValue != null && previousValue." + relation.PascalRoleName + relation.ChildTable.PascalName + "List.Contains(this))");
					sb.AppendLine("			{");
					sb.AppendLine("				previousValue." + relation.PascalRoleName + relation.ChildTable.PascalName + "List.Remove(this);");
					sb.AppendLine("			}");

					sb.AppendLine();
					sb.AppendLine("			if (" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item != null)");
					sb.AppendLine("			{");
					sb.AppendLine("				if (!" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item." + relation.PascalRoleName + relation.ChildTable.PascalName + "List.Contains(this))");
					sb.AppendLine("				{");
					sb.AppendLine("					" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item." + relation.PascalRoleName + relation.ChildTable.PascalName + "List.Add(this);");
					sb.AppendLine("				}");
					sb.AppendLine();

					foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
					{
						sb.AppendLine("				" + columnRelationship.ChildColumn.PascalName + " = " + relation.PascalRoleName + relation.ParentTable.PascalName + "Item." + columnRelationship.ParentColumn.PascalName + ";");
					}

					sb.AppendLine("			}");
					sb.AppendLine("			else if (!skipKeys)");
					sb.AppendLine("			{");

					foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
					{
						if (columnRelationship.ChildColumn.AllowNull)
							sb.AppendLine("				" + columnRelationship.ChildColumn.PascalName + " = null;");
					}

					sb.AppendLine("			}");
					sb.AppendLine();
					sb.AppendLine("			if (ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("			{");
					sb.AppendLine("				if (ChangeTracker.OriginalValues.ContainsKey(\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\")");
					sb.AppendLine("						&& (ChangeTracker.OriginalValues[\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\"] == " + relation.PascalRoleName + relation.ParentTable.PascalName + "Item))");
					sb.AppendLine("				{");
					sb.AppendLine("					ChangeTracker.OriginalValues.Remove(\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\");");
					sb.AppendLine("				}");
					sb.AppendLine("				else");
					sb.AppendLine("				{");
					sb.AppendLine("					ChangeTracker.RecordOriginalValue(\"" + relation.PascalRoleName + relation.ParentTable.PascalName + "\", previousValue);");
					sb.AppendLine("				}");
					sb.AppendLine("				if (" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item != null && !" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item.ChangeTracker.ChangeTrackingEnabled)");
					sb.AppendLine("				{");
					sb.AppendLine("					" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item.StartTracking();");
					sb.AppendLine("				}");
					sb.AppendLine("			}");
					sb.AppendLine("		}");
					sb.AppendLine();
				}
			}
			#endregion

			sb.AppendLine("		#endregion");
			sb.AppendLine();

		}

		private void AppendChangeTracking()
		{
			sb.AppendLine("		#region ChangeTracking");
			sb.AppendLine();
			sb.AppendLine("		protected virtual void OnPropertyChanged(String propertyName)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (ChangeTracker.State != ObjectState.Added && ChangeTracker.State != ObjectState.Deleted)");
			sb.AppendLine("			{");
			sb.AppendLine("				ChangeTracker.State = ObjectState.Modified;");
			sb.AppendLine("			}");
			sb.AppendLine("			if (_propertyChanged != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				_propertyChanged(this, new PropertyChangedEventArgs(propertyName));");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		protected virtual void OnNavigationPropertyChanged(String propertyName)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (_propertyChanged != null)");
			sb.AppendLine("			{");
			sb.AppendLine("				_propertyChanged(this, new PropertyChangedEventArgs(propertyName));");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { add { _propertyChanged += value; } remove { _propertyChanged -= value; } }");
			sb.AppendLine("		private event PropertyChangedEventHandler _propertyChanged;");
			sb.AppendLine("		private ObjectChangeTracker _changeTracker;");
			sb.AppendLine();
			sb.AppendLine("		[DataMember]");
			sb.AppendLine("		public virtual ObjectChangeTracker ChangeTracker");
			sb.AppendLine("		{");
			sb.AppendLine("			get");
			sb.AppendLine("			{");
			sb.AppendLine("				if (_changeTracker == null)");
			sb.AppendLine("				{");
			sb.AppendLine("					_changeTracker = new ObjectChangeTracker();");
			sb.AppendLine("					_changeTracker.ObjectStateChanging += HandleObjectStateChanging;");
			sb.AppendLine("				}");
			sb.AppendLine("				return _changeTracker;");
			sb.AppendLine("			}");
			sb.AppendLine("			set");
			sb.AppendLine("			{");
			sb.AppendLine("				if (_changeTracker != null)");
			sb.AppendLine("				{");
			sb.AppendLine("					_changeTracker.ObjectStateChanging -= HandleObjectStateChanging;");
			sb.AppendLine("				}");
			sb.AppendLine("				_changeTracker = value;");
			sb.AppendLine("				if (_changeTracker != null)");
			sb.AppendLine("				{");
			sb.AppendLine("					_changeTracker.ObjectStateChanging += HandleObjectStateChanging;");
			sb.AppendLine("				}");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		private void HandleObjectStateChanging(object sender, ObjectStateChangingEventArgs e)");
			sb.AppendLine("		{");
			sb.AppendLine("			if (e.NewState == ObjectState.Deleted)");
			sb.AppendLine("			{");
			sb.AppendLine("				ClearNavigationProperties();");
			sb.AppendLine("			}");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		protected bool IsDeserializing { get; private set; }");
			sb.AppendLine();
			sb.AppendLine("		[OnDeserializing]");
			sb.AppendLine("		public void OnDeserializingMethod(StreamingContext context)");
			sb.AppendLine("		{");
			sb.AppendLine("			IsDeserializing = true;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		[OnDeserialized]");
			sb.AppendLine("		public void OnDeserializedMethod(StreamingContext context)");
			sb.AppendLine("		{");
			sb.AppendLine("			IsDeserializing = false;");
			sb.AppendLine("			ChangeTracker.ChangeTrackingEnabled = true;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		protected virtual void ClearNavigationProperties()");
			sb.AppendLine("		{");

			//Where current table is parent
			var relationList = _currentTable.GetRelations();
			foreach (Relation relation in relationList)
			{
				if (!relation.ParentTable.Generated || !relation.ChildTable.Generated)
				{ 
					//Do Nothing
				}

				else if (relation.ChildTable.IsInheritedFrom(relation.ParentTable))
				{
					//Do Nothing
				}

				else if (relation.IsOneToOne)
				{
					sb.AppendLine("			" + relation.PascalRoleName + relation.ChildTable.PascalName + "Item = null;");
				}

				else if (relation.IsManyToMany)
				{
					sb.AppendLine("			" + relation.PascalRoleName + relation.GetSecondaryAssociativeTable().PascalName + "List.Clear();");
				}

				else
				{
					sb.AppendLine("			" + relation.PascalRoleName + relation.ChildTable.PascalName + "List.Clear();");
				}
		
			}

			//Where current table is child
			var relationList2 = _currentTable.GetRelationsWhereChild();
			foreach (Relation relation in relationList2)
			{
				if (!relation.ParentTable.Generated || !relation.ChildTable.Generated)
				{
					//Do Nothing
				}

				else if (relation.ChildTable.IsInheritedFrom(relation.ParentTable))
				{
					//Do Nothing
				}

				else if (relation.IsOneToOne)
				{
					sb.AppendLine("			" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item = null;");
				}

				else if (relation.IsManyToMany)
				{
					sb.AppendLine("			" + relation.PascalRoleName + relation.GetSecondaryAssociativeTable().PascalName + "List.Clear();");
				}

				else
				{
					sb.AppendLine("			" + relation.PascalRoleName + relation.ParentTable.PascalName + "Item = null;");
				}

			}

			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		#endregion");
			sb.AppendLine();
		}

		#endregion

	}
}